package org.xtense.model.actions;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.xtense.engine.IConstants;
import org.xtense.util.StringUtil;

public class UpdateHelper {

	private static UpdateHelper helper;
	
	private UpdateHelper() {
	}
	
	public static UpdateHelper getInstance() {
		if(helper == null) {
			helper = new UpdateHelper();
		}
		return helper;
	}
	
	public List<String> updateForAdd(int documentID, int elementPre, int size) {
		String docIdExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Document_ID, documentID });
		String elePreExpr = MessageFormat.format(IConstants.Sql_Greater_Than,
				new Object[] { IConstants.Element_Pre, elementPre });
		String andExpr = MessageFormat.format(IConstants.Sql_And, new Object[] {
				elePreExpr, docIdExpr });

		String elePreIncrExpr = StringUtil.append(new Object[] {
				IConstants.Element_Pre, " = ", IConstants.Element_Pre, " + ",
				size });

		// First update elementPre in Attribute table, Element table and
		// Text,path
		// table.
		List<String> sql = new ArrayList<String>();
		Iterator<String> iterator;
		for (iterator = IConstants.AETPtables.values().iterator(); iterator
				.hasNext();) {
			sql.add(MessageFormat.format(IConstants.Sql_Update, new Object[] {
					iterator.next(), elePreIncrExpr, andExpr }));
		}

		// Third, update parentPre in Element table.
		String parPreIncrExpr = StringUtil.append(new Object[] {
				IConstants.Parent_Pre, " = ", IConstants.Parent_Pre, " + ",
				size });
		 
		String parPreGTExpr = MessageFormat.format(IConstants.Sql_Greater_Than,
				new Object[] { IConstants.Parent_Pre, elementPre });
		andExpr = MessageFormat.format(IConstants.Sql_And, new Object[] {
				parPreGTExpr, docIdExpr });
		sql.add(MessageFormat.format(IConstants.Sql_Update, new Object[] {
				IConstants.Element_Table, parPreIncrExpr, andExpr }));
		return sql;
	}
	
	public List<String> updateForDelete(int documentID, int elementPre, int size) {
		String docIdExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Document_ID, documentID });
		String elePreExpr = MessageFormat.format(IConstants.Sql_Greater_Than,
				new Object[] { IConstants.Element_Pre, elementPre });
		String andExpr = MessageFormat.format(IConstants.Sql_And, new Object[] {
				elePreExpr, docIdExpr });

		String elePreIncrExpr = StringUtil.append(new Object[] {
				IConstants.Element_Pre, " = ", IConstants.Element_Pre, " - ",
				size });

		// First update elementPre in Attribute table, Element table and
		// Text,path
		// table.
		List<String> sql = new ArrayList<String>();
		Iterator<String> iterator;
		for (iterator = IConstants.AETPtables.values().iterator(); iterator
				.hasNext();) {
			sql.add(MessageFormat.format(IConstants.Sql_Update, new Object[] {
					iterator.next(), elePreIncrExpr, andExpr }));
		}

		// Third, update parentPre in Element table.
		String parPreIncrExpr = StringUtil.append(new Object[] {
				IConstants.Parent_Pre, " = ", IConstants.Parent_Pre, " - ",
				size });
		String parPreGTExpr = MessageFormat.format(IConstants.Sql_Greater_Than,
				new Object[] { IConstants.Parent_Pre, elementPre });
		andExpr = MessageFormat.format(IConstants.Sql_And, new Object[] {
				parPreGTExpr, docIdExpr });
		sql.add(MessageFormat.format(IConstants.Sql_Update, new Object[] {
				IConstants.Element_Table, parPreIncrExpr, andExpr }));
		return sql;
	}

	/**
	 * @param elementPre
	 * @param childElmentName
	 */
	// Refer to the following update sql statement
	//
	// UPDATE element SET elementPre = elementPre + 1
	// WHERE elementPre > 5 AND documentId = 1;
	// UPDATE element SET parentPre = parentPre + 1
	// WHERE parentPre > 5 AND documentId = 1;
	//
	// INSERT INTO element VALUES (element, childElementName, documentId,
	// parentPre)
	public List<String> addElementAfter(int documentID, int elementPre,
			int parentPre, String elementAfterName, String pathValue,
			boolean isChild) {
		List<String> sql = new ArrayList<String>();

		// insert path.
		// INSERT INTO path VALUES (elementPre, documentId, pathValue).
		String valuesExpr = StringUtil.append(new Object[] { elementPre + 1,
				", ", documentID, ", '", pathValue, "'" });
			 
		sql.add(MessageFormat.format(IConstants.Sql_Insert,
				new Object[] {
				IConstants.Path_Table, valuesExpr }));
		
		
		// Finally, insert element.
		if (isChild) {
			parentPre = elementPre;
		}
		
		valuesExpr = StringUtil.append(new Object[] { elementPre + 1, ", '",
				elementAfterName, "', ", documentID, ", ", parentPre });
			 
		sql.add(MessageFormat.format(IConstants.Sql_Insert, new Object[] {
				IConstants.Element_Table, valuesExpr }));

		return sql;
	}
	
	// Refer to the insert statement
	//
	// INSERT INTO attribute
	// VALUES ('attributeName', 'attributeValue', documentId, elementPre)
	public String addAttribute(int documentID, int elementPre,
			String attributeName,
			String attributeValue) {
		String valuesExpr = StringUtil.append(new Object[] { "'",
				attributeName, "', '", attributeValue, "', ", documentID, ", ",
				elementPre });
			 
		String sql = MessageFormat.format(IConstants.Sql_Insert,
				new Object[] {
				IConstants.Attribute_Table, valuesExpr });
		return sql;
	}
	
	// Refer to the insert statement
	//
	// INSERT INTO text
	// VALUES ('textContent', documentID, elementPre)
	public String addText(int documentID, int elementPre, String textContent) {
		String valuesExpr = StringUtil.append(new Object[] { "'", textContent,
				"', ", documentID, ", ", elementPre });
		
		String sql = MessageFormat.format(IConstants.Sql_Insert,
				new Object[] {
				IConstants.Text_Table, valuesExpr });
		return sql;
	}
	
	/**
	 * Change element name
	 * 
	 * @param elementPre
	 * @param newName
	 */
	// Refer to the following sql statement
	//
	// UPDATE element SET elementTag = ?
	// WHERE elementPre = ? and documentId = ?
	public String changeElementName(int documentID, int elementPre,
			String newName) {
		return changeNodeName(documentID, elementPre, newName,
				IConstants.ELEMENT);
	}
	
	/**
	 * change attribute name
	 * 
	 * @param elementPre
	 * @param newName
	 */
	// Refer to the following sql statement
	//
	// UPDATE attribute SET attributeName = ?
	// WHERE elementPre = ? AND documentId = ?
	public String changeAttributeName(int documentID, int elementPre,
			String newName) {
		return changeNodeName(documentID, elementPre, newName,
				IConstants.ATTRIBUTE);
	}
	
	/**
	 * change node name
	 * 
	 * @param elementPre
	 * @param newName
	 * @param nodeType
	 */
	// Refer to the following sql statement
	//
	// UPDATE tableName SET nodeName = ?
	// WHERE elementPre = ? AND documentId = ?
	public String changeNodeName(int documentID, int elementPre,
			String newName,
			int nodeType) {
		String tableName = null;
		String nodeName = null;
		switch (nodeType) {
		case IConstants.ATTRIBUTE:
			tableName = IConstants.Attribute_Table;
			nodeName = IConstants.Attribute_Name;
			break;
		case IConstants.ELEMENT:
			tableName = IConstants.Element_Table;
			nodeName = IConstants.Element_Tag;
			break;
		}
		
		String docIdExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Document_ID, documentID });
		String elePreExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Element_Pre, elementPre });
		String andExpr = MessageFormat.format(IConstants.Sql_And, new Object[] {
				docIdExpr, elePreExpr });
		String nodeNameExpr = MessageFormat.format(IConstants.Sql_String_Equal,
				new Object[] { nodeName, newName });
			
		String sql = MessageFormat.format(IConstants.Sql_Update, new Object[] {
				tableName, nodeNameExpr, andExpr });
		return sql;
	}
	
	// Refer to the following sql statement
	//
	// UPDATE attribute SET attributeValue = ?
	// WHERE elementPre=? AND documentId=?
	public String changeAttributeValue(int documentID, int elementPre,
			String newValue) {
		return this.changeNodeValue(documentID, elementPre, newValue,
				IConstants.ATTRIBUTE);
	}
	
	public String changeTextContent(int documentID, int elementPre,
			String newContent) {
		return this.changeNodeValue(documentID, elementPre, newContent,
				IConstants.TEXT);
	}
	
	// Refer to the following sql statement
	//
	// UPDATE tableName SET nodeValue = ?
	// WHERE elementPre=? AND documentId=?
	private String changeNodeValue(int documentID, int elementPre,
			String newValue, int nodeType) {
		String tableName = null;
		String nodeValue = null;
		switch (nodeType) {
		case IConstants.ATTRIBUTE:
			tableName = IConstants.Attribute_Table;
			nodeValue = IConstants.Attribute_Value;
			break;
		case IConstants.TEXT:
			tableName = IConstants.Text_Table;
			nodeValue = IConstants.Text_Content;
			break;
		}

		String docIdExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Document_ID, documentID });
		String elePreExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Element_Pre, elementPre });
		String andExpr = MessageFormat.format(IConstants.Sql_And, new Object[] {
				docIdExpr, elePreExpr });
		
		String nodeValueExpr = MessageFormat.format(
				IConstants.Sql_String_Equal,
				new Object[] { nodeValue, newValue });
		
		String sql = MessageFormat.format(IConstants.Sql_Update, new Object[] {
				tableName, nodeValueExpr, andExpr });
		return sql;
	}
	
	// Refer to the following sql statement
	//
	// DELETE FROM attribute
	// WHERE attributeName = ? AND elementPre = ? AND doucumentId = ?
	public String removeAttribute(int documentID, int elementPre,
			String attributeName) {
		String docIdExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Document_ID, documentID });
		String elePreExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Element_Pre, elementPre });
		String andExpr = MessageFormat.format(IConstants.Sql_And, new Object[] {
				docIdExpr, elePreExpr });
		String attrNameExpr = MessageFormat.format(IConstants.Sql_String_Equal,
				new Object[] { IConstants.Attribute_Name, attributeName });
		andExpr = MessageFormat.format(IConstants.Sql_And, new Object[] {
				attrNameExpr, andExpr });
		
		String sql = MessageFormat.format(IConstants.Sql_Delete,
				new Object[] {
				IConstants.Attribute_Table, andExpr });
		
		return sql;
	}

	// Refer to the following sql statement
	//
	// DELETE FROM text
	// WHERE elementPre = ? AND documentId = ?
	public String removeText(int documentID, int elementPre) {
		String docIdExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Document_ID, documentID });
		String elePreExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Element_Pre, elementPre });
		String andExpr = MessageFormat.format(IConstants.Sql_And, new Object[] {
				docIdExpr, elePreExpr });
		
		String sql = MessageFormat.format(IConstants.Sql_Delete,
				new Object[] {
				IConstants.Text_Table, andExpr });

		return sql;
	}
	
	// Refer to the following sql statement
	//
	// DELETE FROM element
	// WHERE elementPre = ? AND documentId = ?
	//
	// DELETE FROM PATH
	// WHERE elementPre= ? AND documentId = ?
	//
	// first delete then update
	public List<String> removeElement(int documentID, int elementPre) {
		
		String docIdExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Document_ID, documentID });
		String elePreExpr = MessageFormat.format(IConstants.Sql_Int_Equal,
				new Object[] { IConstants.Element_Pre, elementPre });
		String andExpr = MessageFormat.format(IConstants.Sql_And, new Object[] {
				docIdExpr, elePreExpr });
		
		List<String> sql = new ArrayList<String>();

		sql.add(MessageFormat.format(IConstants.Sql_Delete, new Object[] {
				IConstants.Path_Table, andExpr }));
		sql.add(MessageFormat.format(IConstants.Sql_Delete, new Object[] {
				IConstants.Element_Table, andExpr }));
		
		return sql;
	}
}
